﻿using Log2File;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace MessagePush
{
    class Client
    {
        private string name;
        private string message;
        private Socket currentSocket;//保存与当前用户连接的Socket对象  
        private string ipAddress;
        private ClientSeverForm server;

        public ClientSeverForm Server
        {
            get { return server; }
            set { server = value; }
        }
        //保存当前链接的状态:closed         
        private string state = "closed";

        public Client(ClientSeverForm server, Socket clientSocket)
        {
            this.server = server;
            this.currentSocket = clientSocket;
            ipAddress = getRemoteIPAddress();
        }

        public Client(ClientSeverForm server, string userName, string message)
        {
            this.server = server;
            this.name = userName;
            this.message = message;
            //ipAddress = getRemoteIPAddress();
        }


        private DateTime lastHeartbeatTime;
        public DateTime LastHeartbeatTime()
        {
            return lastHeartbeatTime;
        }


        public string Name
        {
            get
            {
                return name;
            }
            set
            {
                name = value;
            }
        }

        public Socket CurrentSocket
        {
            get
            {
                return currentSocket;
            }
            set
            {
                currentSocket = value;
            }
        }

        public string IpAddress
        {
            get
            {
                return ipAddress;
            }
        }

        //获取ip地址  
        private string getRemoteIPAddress()
        {
            return ((IPEndPoint)currentSocket.RemoteEndPoint).Address.ToString();
        }

        //服务器处理聊天命令时，需要向客户端发送命令或者发送响应信息，这些命令的发送都是通过SendToClient()函数实现的  
        //SendToClient()方法实现了向客户端发送命令请求的功能  
        private void SendToClient(Client client, string msg)
        {

            System.Byte[] message = System.Text.Encoding.Default.GetBytes(msg.ToCharArray());
            client.CurrentSocket.Send(message, message.Length, 0);
            //client.CurrentSocket.
        }

        /// <summary>  
        /// Client类中提供了一个ServiceClient()函数，用于与客户端进行通信，接受客户端请求  
        /// 根据不同命令执行不同操作，将结果返回给客户端  
        /// </summary>  
        public void ServiceClient()
        {
            string[] tokens = null;
            byte[] buff = new byte[1024];
            bool keepConnect = true;//退出循环，关闭连接  
            //循环的与客户端交互，直到客户端发送exit  
            //讲keepConnect跟新为false ,推出循环，关闭连  
            while (keepConnect && ClientSeverForm.SocketServiceFlag)
            {
                try
                {
                    if (this.CurrentSocket == null || this.CurrentSocket.Available < 1)
                    {
                        Thread.Sleep(300);
                        continue;
                    }
                    //接收数据并保存入buff数组中  
                    int len = this.CurrentSocket.Receive(buff);
                    //讲字符数组转为字符串，  
                    string clientCommand = System.Text.Encoding.Default.GetString(buff, 0, len);
                    //tokens[0]中保存了命令标志符（CONN，ＣＨＡＴ，ＰＲＩＶ，ＬＩＳＴ，ＥＸＩＴ）  
                    tokens = clientCommand.Split(new Char[] { '|' });
                    if (tokens == null)
                    {
                        Thread.Sleep(200);
                        continue;
                    }
                }
                catch (Exception e)
                {
                    Server.updateUI("发生异常");
                }

                //以上代码是用于服务器初始化和接收客户端发来的请求，对数据解析后，把命令转为数组形式  
                //下面讲服务器根据不同的命令进行相应的处理  
                if (tokens[0] == "CONN")
                {
                    //此时接收到的命令格式为  
                    //命令标识符（CONN）发送者的用户名  
                    //tokens[1]中保存发送者的用户名  
                    this.Name = tokens[1];
                    if (ClientSeverForm.clients.Contains(this.name))
                    {
                        SendToClient(this, "ERR|User" + this.name + "已经存在");
                    }
                    else
                    {
                        

                        Hashtable synclients = Hashtable.Synchronized(ClientSeverForm.clients);
                        synclients.Add(this.name, this);
                        // 更新界面  
                        server.addUser(this.name);

                        //对每一个当前在线的用户发送JOIN消息命令和LIST息命令  
                        //以此来更新客户端的当前在线用户列表  
                        System.Collections.IEnumerator myEnumerator = ClientSeverForm.clients.Values.GetEnumerator();
                        while (myEnumerator.MoveNext())
                        {
                            Client client = (Client)myEnumerator.Current;
                            SendToClient(client, "JOIN|" + tokens[1] + "|");
                            Thread.Sleep(100);
                        }
                        //更新状态  
                        state = "connected";
                        SendToClient(this, "ok");

                        //向客户端发t送list命令，以此跟新客户端的当前在线用户列表  
                        string msgUsers = "LIST|" + server.GetUserList();
                        SendToClient(this, msgUsers);
                        this.lastHeartbeatTime = DateTime.Now;
                        Log.log("CONN");
                        Log.log("LastHeartbeatTime=" + this.lastHeartbeatTime.ToLongTimeString());
                    }
                }
                else if (tokens[0] == "LIST")
                {
                    if (state == "connected")
                    {
                        //向客户端发送list命令，以此跟新用户列表
                        string msgUsers = "LIST|" + server.GetUserList();
                        SendToClient(this, msgUsers);
                        this.lastHeartbeatTime = DateTime.Now;
                    }
                    else
                    {
                        SendToClient(this, "ERR|state error,Please login first");
                    }
                }
                else if (tokens[0] == "CHAT")
                {
                    if (state == "connected")
                    {
                        
                        //此时接收到的命令的格式为：  
                        //命令标识符，发送者的用户名：发送内容|向所有当前在线的用户转发此消息  
                        //System.Collections.IEnumerator myEnumerator = ClientSeverForm.clients.Values.GetEnumerator();//ChatClientForm.clients.Values.GetEnumerator();  
                        //while (myEnumerator.MoveNext())
                        //{
                        //    Client client = (Client)myEnumerator.Current;
                        //    //讲“发送者的用户名：发送内容“转发给用户””  
                        //    SendToClient(client, tokens[2]);
                        //}
                        //server.updateUI(tokens[1] + "2server:" + tokens[2]);
                        //this.lastHeartbeatTime = DateTime.Now;
                        //Log.log("CHAT");
                        //Log.log("LastHeartbeatTime=" + this.lastHeartbeatTime.ToLongTimeString());


                        this.lastHeartbeatTime = DateTime.Now;

                        Client client = (Client)ClientSeverForm.clients[tokens[1]];
                        string sender = tokens[1];

                        string message = "HeartBeat from " + sender + ",now=" + lastHeartbeatTime.ToLongTimeString();
                        server.updateUI(message);

                        string message2 = "HEART|" + sender;
                        SendToClient(client, message2);



                        Log.log("HEART,tokens=" + tokens);
                        Log.log("LastHeartbeatTime=" + this.lastHeartbeatTime.ToLongTimeString());

                    }
                    else
                    {
                        SendToClient(this, "ERR|state error,Please login first");
                    }
                }
                //如果是PRIV命令，就把信息转发给PRIV命令中指定的接受者和发送者  
                else if (tokens[0] == "PRIV")
                {
                    if (state == "connected")
                    {
                        //此时接收到的命令格式为  
                        //命令标识符（ＰＲＩＶ）发送呢者用户名（接受者用户名|发送内容|tokens[1]中保存了发送者的用户名）ro  
                        string sender = tokens[1];
                        //tokens[2] 中保存了接受者 的名字  
                        string receiver = tokens[2];
                        //tokens[3]保存了发送的内容  
                        string content = tokens[3];
                        string message = sender + "2" + receiver + ":" + content;
                        //仅将信息转发给发送者和接受者  
                        if (ClientSeverForm.clients.Contains(sender))
                        {
                            SendToClient((Client)ClientSeverForm.clients[sender], message);

                        }
                        if (ClientSeverForm.clients.Contains(receiver))
                        {
                            SendToClient((Client)ClientSeverForm.clients[receiver], message);
                        }
                        server.updateUI(message);
                    }
                    else
                    {
                        SendToClient(this, "ERR|state error,Please login first");
                    }
                }
                else if (tokens[0] == "EXIT")
                {
                    //此时接收到的命令的格式：命令标石符|发送者的名字|  
                    //向所有当前用户发送已经离线的信息  
                    if (ClientSeverForm.clients.Contains(tokens[1]))
                    {
                        
                        Client client = (Client)ClientSeverForm.clients[tokens[1]];

                        //将该用户对应的Client对象从clients中删除  
                        Hashtable syncClients = Hashtable.Synchronized(ClientSeverForm.clients);
                        syncClients.Remove(client.Name);
                        server.removeUser(client.Name);

                        //向客户端发送QUIT命令  
                        string message = "QUIT|" + tokens[1];
                        System.Collections.IEnumerator myEnumberator = ClientSeverForm.clients.Values.GetEnumerator();
                        while (myEnumberator.MoveNext())
                        {
                            Client c = (Client)myEnumberator.Current;
                            SendToClient(c, message);
                        }
                        server.updateUI(message);
                        Log.log("EXIT");
                        Log.log("LastHeartbeatTime=" + this.lastHeartbeatTime.ToLongTimeString());
                    }
                    break;
                }
                else if (tokens[0] == "HEART")
                {
                    if (ClientSeverForm.clients.Contains(tokens[1]))
                    {

                        //this.lastHeartbeatTime = DateTime.Now;

                        //Client client = (Client)ClientSeverForm.clients[tokens[1]];
                        //string sender = tokens[1];

                        //string message = "HeartBeat from " + sender + ",now=" + lastHeartbeatTime.ToLongTimeString();
                        //server.updateUI(message);

                        //string message2 = "HEART|" + sender;
                        //SendToClient(client, message2);

                        

                        //Log.log("HEART,tokens=" + tokens);
                        //Log.log("LastHeartbeatTime=" + this.lastHeartbeatTime.ToLongTimeString());
                        
                    }
                    break;
                }
                Thread.Sleep(200);
            }
        }


        public void Server2Client_quit()
        {
            if (ClientSeverForm.clients.Contains(name))
            {
                Client cc = (Client)ClientSeverForm.clients[name];

                //向客户端发送QUIT命令  
                string message = "QUIT|" + name;
                SendToClient(cc, message);
                server.updateUI("server2" + name + ":" + message);
            }
        }


        public void Server2Client()
        {
            if (ClientSeverForm.clients.Contains(name))
            {
                Client cc = (Client)ClientSeverForm.clients[name];
                SendToClient(cc, message);
                server.updateUI("server2" + name + ":" + message);
            }
            else
            {
                server.updateUI("send data to client error.");
            }
        }
    }
}
